# _*_ coding: utf-8 _*_
"""
Time:     2024/8/22
Author:   2051261
File:     shutdown.py
"""
import signal
import time
import logging


logger = logging.getLogger(__name__)


def run_background(scheduler, interval=None, shutdown=None):
    if shutdown is None:
        shutdown = ShutdownDefault(scheduler)

    logger.info('start')
    interval = 3 if interval is None else interval
    while True:
        time.sleep(interval)
        sig = shutdown.signal
        if sig is None:
            continue

        done = shutdown.handle(sig)
        if done:
            break

        logger.info('end')
        return 0


class Shutdown(object):

    def __init__(self, scheduler, signals=None):
        self.scheduler = scheduler
        self._signal = None
        self._handlers = None
        self.signals = tuple(signals or [])

        scheduler.start()
        for sig in self.signals:
            signal.signal(sig, self.set_signal)

        _ = self.handlers

    @property
    def signal(self):
        sig = self._signal
        self._signal = None
        return sig

    def set_signal(self, sig, frame):
        _ = frame
        logger.info(f'[signal:{signal.strsignal(sig)}({sig})] receive')
        self._signal = sig

    @property
    def handlers(self):
        # 实例化时自动生成/解析 handlers
        if self._handlers is not None:
            return self._handlers

        # 映射 handler 后缀到 handler 方法
        tmp = {}
        prefix = 'handle_'
        for name in dir(self):
            if not name.startswith(prefix):
                continue
            if name == 'handle_unknown':
                continue
            attr = getattr(self, name)
            if not callable(attr):
                continue
            tmp[name.lstrip(prefix).lower()] = attr

        # 映射 signal 到 handler 方法
        handlers = {}
        ks = sorted(tmp)
        for sig in self.signals:
            name = signal.strsignal(sig).lower()
            # 使用信号字符串匹配 handler 后缀
            for k in ks:
                if name.startswith(k):
                    handlers[sig] = tmp[k]

            if sig not in handlers:
                logger.warning(
                    f'[signal:{signal.strsignal(sig)}({sig})] '
                    f'no handler registered for signal'
                )

        self._handlers = handlers
        return self._handlers

    def handle(self, sig):
        handler = self.handlers.get(sig, self.handle_unknown)
        return handler(sig)

    def handle_unknown(self, sig):
        _ = self
        logger.info(f'[signal:{sig}] skip unknown signal')
        return False


class ShutdownDefault(Shutdown):

    def __init__(self, scheduler, signals=None):
        super(ShutdownDefault, self).__init__(
            scheduler=scheduler,
            signals=signals or (signal.SIGINT, signal.SIGTERM),
        )

    def handle_int(self, sig):
        logger.info(f'[signal:{signal.strsignal(sig)}({sig})] wait for shutdown')
        self.scheduler.shutdown(wait=True)
        return True

    def handle_term(self, sig):
        return self.handle_int(sig)
